package com.google.code.swingonfire.utility.date;

import com.google.code.swingonfire.utility.Scanner;
import com.google.code.swingonfire.utility.Tokenizer;

public class DateRenderInfoTokenizer extends Tokenizer
{

    public DateRenderInfoTokenizer(char[] source, int offset, int length)
    {
        super(source, offset, length);
    }

    public DateRenderInfoTokenizer(char[] source)
    {
        super(source);
    }

    public DateRenderInfoTokenizer(String source, int offset, int length)
    {
        super(source, offset, length);
    }

    public DateRenderInfoTokenizer(String source)
    {
        super(source);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Token parse(Scanner scanner, int line, int column)
    {
        char ch = scanner.getChar();

        if (isLetter(ch))
        {
            char symbol = ch;
            int length = 1;

            while (true)
            {
                ch = scanner.next();

                if (ch != symbol)
                {
                    return createSymbolToken(line, column, symbol, length);
                }

                length += 1;
            }
        }
        else if (ch == '\'')
        {
            StringBuilder builder = new StringBuilder();

            while (true)
            {
                ch = scanner.next();

                if (ch == Scanner.END_OF_SOURCE)
                {
                    // unclosed, ignore
                    return createTextToken(line, column, builder.toString());
                }
                else if (ch == '\'')
                {
                    ch = scanner.next();

                    if (ch != '\'')
                    {
                        if (builder.length() == 0)
                        {
                            // escaped quote (outside of quoted text)
                            return createTextToken(line, column, "\'");
                        }

                        // no escaped quote, otherwise fallthrough
                        return createTextToken(line, column, builder.toString());
                    }
                }

                builder.append(ch);
            }
        }
        else
        {
            StringBuilder builder = new StringBuilder();

            while (true)
            {
                builder.append(ch);

                ch = scanner.next();

                if ((isLetter(ch) || (ch == '\'')) || (ch == Scanner.END_OF_SOURCE))
                {
                    return createTextToken(line, column, builder.toString());
                }
            }
        }
    }

    private Token createSymbolToken(int line, int column, char symbol, int length)
    {
        switch (symbol)
        {
            case 'G':
                return new Token(new DateRenderInfoEraPart(length), line, column);

            case 'y':
                return new Token(new DateRenderInfoYearPart(length), line, column);

            case 'M':
                return new Token(new DateRenderInfoMonthPart(length), line, column);

            case 'w':
                return new Token(new DateRenderInfoWeekPart(length), line, column);

            case 'W':
                return new Token(new DateRenderInfoWeekOfMonthPart(length), line, column);

            case 'D':
                return new Token(new DateRenderInfoDayOfYearPart(length), line, column);

            case 'd':
                return new Token(new DateRenderInfoDayPart(length), line, column);

            case 'F':
                return new Token(new DateRenderInfoDayOfWeekInMonthPart(length), line, column);

            case 'E':
                return new Token(new DateRenderInfoDayOfWeekPart(length), line, column);

            case 'a':
                return new Token(new DateRenderInfoAmPmMarkerPart(length), line, column);

            case 'H':
                return new Token(new DateRenderInfoHourOfDayPart(length), line, column);

            case 'k':
                return new Token(new DateRenderInfoAlternateHourOfDayPart(length), line, column);

            case 'K':
                return new Token(new DateRenderInfoAlternateHourPart(length), line, column);

            case 'h':
                return new Token(new DateRenderInfoHourPart(length), line, column);

            case 'm':
                return new Token(new DateRenderInfoMinutePart(length), line, column);

            case 's':
                return new Token(new DateRenderInfoSecondPart(length), line, column);

            case 'S':
                return new Token(new DateRenderInfoMillisecondPart(length), line, column);

            case 'z':
                return new Token(new DateRenderInfoTimeZonePart(length), line, column);

            case 'Z':
                return new Token(new DateRenderInfoRFC822TimeZonePart(length), line, column);

            default:
                throw new IllegalArgumentException("Invalid symbol at column " + column + " of pattern: " + this);
        }
    }

    private Token createTextToken(int line, int column, String text)
    {
        return new Token(new DateRenderInfoConstantPart(text), line, column);
    }

    private boolean isLetter(char ch)
    {
        return ((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'));
    }

}